Express 与 TypeScript 教程
1. TypeScript 简介和优势
1.1 什么是 TypeScript?
TypeScript 是 JavaScript 的超集,添加了静态类型定义和其他高级功能。它由 Microsoft 开发,旨在提高代码的可维护性和可读性。
1.2 TypeScript 的优势
- 静态类型检查:在编译时捕获错误,减少运行时错误。
- 更好的工具支持:IDE 可以提供更好的代码补全和重构支持。
- 可读性和可维护性:类型定义使代码更易于理解和维护。
2. 开发环境搭建
2.1 安装 Node.js
首先,确保你已经安装了 Node.js。你可以从 Node.js 官网 下载并安装。
2.2 安装 TypeScript 编译器
使用 npm 安装 TypeScript 编译器:
npm install -g typescript
2.3 创建项目目录
创建一个新的项目目录并初始化 npm:
mkdir my-express-app
cd my-express-app
npm init -y
2.4 安装 Express 和 TypeScript 依赖
安装 Express 和 TypeScript 相关的依赖:
npm install express
npm install --save-dev typescript @types/node @types/express
3. 第一个 TypeScript 程序
3.1 创建 tsconfig.json
在项目根目录下创建 tsconfig.json 文件:
{
"compilerOptions": {
"target": "ES6",
"module": "commonjs",
"strict": true,
"esModuleInterop": true,
"outDir": "./dist"
},
"include": ["src/**/*"]
}
3.2 编写第一个 TypeScript 程序
在 src 目录下创建 index.ts 文件:
import express from 'express';
const app = express();
const port = 3000;
app.get('/', (req, res) => {
res.send('Hello, TypeScript with Express!');
});
app.listen(port, () => {
console.log(`Server is running on http://localhost:${port}`);
});
3.3 编译和运行
使用 TypeScript 编译器编译代码:
tsc
编译完成后,运行生成的 JavaScript 文件:
node dist/index.js
打开浏览器访问 http://localhost:3000,你应该会看到 "Hello, TypeScript with Express!"。
4. 基本类型和变量声明
4.1 基本类型
TypeScript 支持以下基本类型:
numberstringbooleanarraytupleenumanyunknownvoidnullundefinednever
4.2 变量声明
let age: number = 25;
let name: string = "Alice";
let isStudent: boolean = true;
let hobbies: string[] = ["reading", "coding"];
let person: [string, number] = ["Alice", 25];
5. TypeScript 配置文件 (tsconfig.json)
tsconfig.json 文件用于配置 TypeScript 编译器的行为。常见的配置选项包括:
target:指定编译后的 JavaScript 版本。module:指定模块系统。strict:启用严格模式。outDir:指定输出目录。
6. 基本类型详解
6.1 number
let age: number = 25;
6.2 string
let name: string = "Alice";
6.3 boolean
let isStudent: boolean = true;
6.4 array
let hobbies: string[] = ["reading", "coding"];
6.5 tuple
let person: [string, number] = ["Alice", 25];
7. 枚举类型
枚举类型用于定义一组命名的常量:
enum Color {
Red,
Green,
Blue
}
let c: Color = Color.Green;
8. Any 和 Unknown 类型
8.1 any
any 类型允许变量存储任何类型的值:
let notSure: any = 4;
notSure = "maybe a string instead";
8.2 unknown
unknown 类型类似于 any,但更安全:
let notSure: unknown = 4;
if (typeof notSure === "string") {
console.log(notSure.length);
}
9. Void, Null 和 Undefined
9.1 void
void 类型通常用于函数没有返回值的情况:
function warnUser(): void {
console.log("This is a warning message");
}
9.2 null 和 undefined
let u: undefined = undefined;
let n: null = null;
10. Never 类型
never 类型表示永远不会发生的值:
function error(message: string): never {
throw new Error(message);
}
11. 类型断言
类型断言允许你告诉编译器某个值的类型:
let someValue: any = "this is a string";
let strLength: number = (someValue as string).length;
12. 函数声明和表达式
12.1 函数声明
function add(x: number, y: number): number {
return x + y;
}
12.2 函数表达式
let add = function(x: number, y: number): number {
return x + y;
};
13. 可选参数和默认参数
13.1 可选参数
function buildName(firstName: string, lastName?: string) {
if (lastName) {
return firstName + " " + lastName;
} else {
return firstName;
}
}
13.2 默认参数
function buildName(firstName: string, lastName = "Smith") {
return firstName + " " + lastName;
}
14. 剩余参数
剩余参数允许你将多个参数收集到一个数组中:
function buildName(firstName: string, ...restOfName: string[]) {
return firstName + " " + restOfName.join(" ");
}
15. 函数重载
函数重载允许你定义多个函数签名:
function add(x: number, y: number): number;
function add(x: string, y: string): string;
function add(x: any, y: any): any {
return x + y;
}
16. This 和箭头函数
16.1 this
let deck = {
suits: ["hearts", "spades", "clubs", "diamonds"],
cards: Array(52),
createCardPicker: function() {
return () => {
let pickedCard = Math.floor(Math.random() * 52);
let pickedSuit = Math.floor(pickedCard / 13);
return {suit: this.suits[pickedSuit], card: pickedCard % 13};
};
}
};
16.2 箭头函数
let deck = {
suits: ["hearts", "spades", "clubs", "diamonds"],
cards: Array(52),
createCardPicker: function() {
return () => {
let pickedCard = Math.floor(Math.random() * 52);
let pickedSuit = Math.floor(pickedCard / 13);
return {suit: this.suits[pickedSuit], card: pickedCard % 13};
};
}
};
17. 接口定义和使用
接口用于定义对象的形状:
interface Person {
firstName: string;
lastName: string;
}
function greeter(person: Person) {
return "Hello, " + person.firstName + " " + person.lastName;
}
18. 可选属性和只读属性
18.1 可选属性
interface SquareConfig {
color?: string;
width?: number;
}
18.2 只读属性
interface Point {
readonly x: number;
readonly y: number;
}
19. 函数类型接口
函数类型接口用于定义函数的形状:
interface SearchFunc {
(source: string, subString: string): boolean;
}
20. 类类型接口
类类型接口用于定义类的形状:
interface ClockInterface {
currentTime: Date;
setTime(d: Date): void;
}
class Clock implements ClockInterface {
currentTime: Date = new Date();
setTime(d: Date) {
this.currentTime = d;
}
}
21. 继承接口
接口可以继承其他接口:
interface Shape {
color: string;
}
interface Square extends Shape {
sideLength: number;
}
22. 类型别名 vs 接口
类型别名和接口都可以用于定义类型,但有一些区别:
type Name = string;
type NameResolver = () => string;
type NameOrResolver = Name | NameResolver;
23. 类的定义和实例化
23.1 类的定义
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet() {
return "Hello, " + this.greeting;
}
}
23.2 类的实例化
let greeter = new Greeter("world");
24. 继承和多态
24.1 继承
class Animal {
move(distanceInMeters: number = 0) {
console.log(`Animal moved ${distanceInMeters}m.`);
}
}
class Dog extends Animal {
bark() {
console.log("Woof! Woof!");
}
}
24.2 多态
let dog = new Dog();
dog.bark();
dog.move(10);
25. 访问修饰符 (public, private, protected)
25.1 public
class Animal {
public name: string;
public constructor(theName: string) { this.name = theName; }
public move(distanceInMeters: number) {
console.log(`${this.name} moved ${distanceInMeters}m.`);
}
}
25.2 private
class Animal {
private name: string;
constructor(theName: string) { this.name = theName; }
}
25.3 protected
class Person {
protected name: string;
constructor(name: string) { this.name = name; }
}
class Employee extends Person {
private department: string;
constructor(name: string, department: string) {
super(name);
this.department = department;
}
public getElevatorPitch() {
return `Hello, my name is ${this.name} and I work in ${this.department}.`;
}
}
26. 静态属性和方法
静态属性和方法属于类本身,而不是类的实例:
class Grid {
static origin = {x: 0, y: 0};
calculateDistanceFromOrigin(point: {x: number; y: number}) {
let xDist = (point.x - Grid.origin.x);
let yDist = (point.y - Grid.origin.y);
return Math.sqrt(xDist * xDist + yDist * yDist) / this.scale;
}
constructor (public scale: number) { }
}
27. 抽象类
抽象类不能直接实例化,通常用作其他类的基类:
abstract class Department {
constructor(public name: string) {
}
printName(): void {
console.log("Department name: " + this.name);
}
abstract printMeeting(): void; // 必须在派生类中实现
}
class AccountingDepartment extends Department {
constructor() {
super("Accounting and Auditing"); // 在派生类的构造函数中必须调用 super()
}
printMeeting(): void {
console.log("The Accounting Department meets each Monday at 10am.");
}
generateReports(): void {
console.log("Generating accounting reports...");
}
}
28. 交叉类型
交叉类型用于组合多个类型:
function extend<T, U>(first: T, second: U): T & U {
let result = <T & U>{};
for (let id in first) {
(<any>result)[id] = (<any>first)[id];
}
for (let id in second) {
if (!result.hasOwnProperty(id)) {
(<any>result)[id] = (<any>second)[id];
}
}
return result;
}
29. 联合类型
联合类型允许变量具有多种类型之一:
function padLeft(value: string, padding: string | number) {
if (typeof padding === "number") {
return Array(padding + 1).join(" ") + value;
}
if (typeof padding === "string") {
return padding + value;
}
throw new Error(`Expected string or number, got '${padding}'.`);
}
30. 类型保护和类型区分
类型保护用于在运行时确定变量的类型:
function isFish(pet: Fish | Bird): pet is Fish {
return (<Fish>pet).swim !== undefined;
}
31. 可为 null 的类型
TypeScript 支持可为 null 的类型:
let s = "foo";
s = null; // 错误, 'null' 不能赋值给 'string'
let sn: string | null = "bar";
sn = null; // 正确
32. 字面量类型
字面量类型允许你定义具体的值作为类型:
let x: "hello" = "hello";
x = "hello"; // 正确
x = "howdy"; // 错误
33. 索引类型和映射类型
33.1 索引类型
function pluck<T, K extends keyof T>(o: T, names: K[]): T[K][] {
return names.map(n => o[n]);
}
33.2 映射类型
type Readonly<T> = {
readonly [P in keyof T]: T[P];
}
34. 泛型函数
泛型函数允许你编写可以处理多种类型的函数:
function identity<T>(arg: T): T {
return arg;
}
35. 泛型接口
泛型接口允许你定义可以处理多种类型的接口:
interface GenericIdentityFn<T> {
(arg: T): T;
}
36. 泛型类
泛型类允许你定义可以处理多种类型的类:
class GenericNumber<